package inner_tos

import (
	"context"
	"io"
	"os"
	"time"

	bytedtracer "code.byted.org/bytedtrace/interface-go"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/biz_error"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/trace"
	"code.byted.org/gopkg/logs/v2/log"
	"code.byted.org/gopkg/tos"
	"github.com/cenkalti/backoff/v4"
	"github.com/pkg/errors"
)

type Client struct {
	cli    *tos.Tos
	config *Config
}

type Config struct {
	BucketName string
	AccessKey  string
}

func New(config *Config) (*Client, error) {
	cli, err := tos.NewTos(tos.WithBucket(config.BucketName), tos.WithCredentials(&tos.BucketAccessKeyCredentials{
		BucketName: config.BucketName,
		AccessKey:  config.AccessKey,
	}))
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return &Client{
		cli:    cli,
		config: config,
	}, nil
}

func (c *Client) UploadFile(ctx context.Context, filename, key string) error {
	f, err := os.Open(filename)
	if err != nil {
		return errors.Wrap(err, "UploadFile failed")
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil {
		return errors.Wrap(err, "UploadFile failed")
	}

	return c.Upload(ctx, key, f, stat.Size(), "application/octet-stream")
}

func (c *Client) Upload(ctx context.Context, key string, reader io.Reader, size int64, contentType string) (err error) {
	span, ctx := bytedtracer.StartCustomSpan(ctx, trace.INTERNAL, trace.CompactUpload,
		bytedtracer.EnableEmitSpanMetrics)
	defer func() {
		span.SetTags(bytedtracer.NewTagKV(trace.IsError, err != nil),
			bytedtracer.NewTagKV(trace.BucketName, c.config.BucketName),
			bytedtracer.NewTagKV(trace.DataLength, size))
		span.Finish()
	}()

	strategy := backoff.WithMaxRetries(backoff.WithContext(backoff.NewConstantBackOff(time.Millisecond*50), ctx), 3)
	err = backoff.RetryNotify(func() error {
		return c.cli.PutObject(ctx, key, size, reader, tos.ContentType(contentType))
	}, strategy, func(err error, duration time.Duration) {
		log.V1.CtxWarn(ctx, "fail to upload %s to %s, len %d, err %+v", key, c.config.BucketName, size, err)
	})
	return biz_error.UploadError.Wrap(errors.WithStack(err))
}

func (c *Client) URL(ctx context.Context, key string) string {
	//使用cdn拼接url
	//如果是用户自定义，要用户配置好cdn，传入cdn域名。
	return ""
}

func (c *Client) PreSignURL(ctx context.Context, key string, duration time.Duration) (string, error) {
	return "", errors.New("inner tos not support presign url")
}
